import * as React from "react";
import classNames from "classnames/bind";
import * as styles from "./editors-view.css";
import { IEditorsState, IEditor } from "../../core/model";
import { dispatch as appDispatch } from "../../core/app-state";
import * as monaco from 'monaco-editor';

const cx = classNames.bind(styles);

function CodeEditorView (props: { editor: IEditor; isActive: boolean }) {
  const { editor, isActive } = props;
  const [ destroyCbs, _ ] = React.useState<(() => void)[]>([]);

  React.useEffect(() => {
    return () => {
      destroyCbs.forEach(cb => cb());
    };
  }, []);
  
  const refCb = React.useCallback((node: HTMLDivElement) => {
    if (node) {
      const codeEditor = monaco.editor.create(node, {
        value: editor.originContent,
        language: editor.lang,
        theme: 'vs-dark',
        minimap: {
          enabled: false,
        },
        folding: true,
      });

      // set resize
      const onResize = (ev: UIEvent) => {
        codeEditor.layout();
      };
      window.addEventListener('resize', onResize);
      destroyCbs.push(() => {
        window.removeEventListener('resize', onResize);
      });

      // set frame resize
      const onFrameResize = (ev: UIEvent) => {
        codeEditor.layout();
      };
      window.addEventListener('app-frame-resize', onFrameResize);
      destroyCbs.push(() => {
        window.removeEventListener('app-frame-resize', onFrameResize);
      });

      // set save hot key
      const onKeyDown = (ev: KeyboardEvent) => {
        if (!ev.ctrlKey || ev.shiftKey || ev.altKey || ev.keyCode != 83) {
          return;
        }
        ev.preventDefault();
        appDispatch({
          type: 'editor.save',
          data: editor.id,
        });
      }
      node.addEventListener('keydown', onKeyDown);
      destroyCbs.push(() => {
        node.removeEventListener('keydown', onKeyDown);
      });

      const disposable = codeEditor.onDidChangeModelContent((e) => {
        appDispatch({
          type: 'editor.changed',
          data: {
            id: editor.id,
            content: codeEditor.getValue(),
          }
        })
      });
      destroyCbs.push(() => {
        disposable.dispose();
      });

      // listen cursor change
      const cursorDisposable = codeEditor.onDidChangeCursorPosition((e) => {
        const selectedText = codeEditor.getModel().getValueInRange(codeEditor.getSelection());
        appDispatch({
          type: 'editor.cursorChanged',
          data: {
            id: editor.id,
            rowNo: e.position.lineNumber,
            colNo: e.position.column,
            selected: selectedText ? selectedText.length : 0
          }
        });
      });
      destroyCbs.push(() => {
        cursorDisposable.dispose();
      });
    }
  }, []);

  return (
    <div className={cx('editor-view', { 'is-show': isActive })}>
      <div ref={refCb} className={cx('the-monaco-editor')}></div>
    </div>
  );
}

function EditorTabsView (props: { editorsState: IEditorsState }) {
  const { editorsState } = props;
  const { editors, activeId } = editorsState;
  
  const onLabelClick = (e: React.MouseEvent<HTMLDivElement>) => {
    const target = e.currentTarget;
    const id = target.getAttribute('id');
    appDispatch({
      type: 'editor.active',
      data: id,
    });
  };

  const onWheel = (e: React.WheelEvent<HTMLDivElement>) => {
    const target = e.currentTarget;
    const scrollLeftMax = target.scrollWidth - target.clientWidth;
    let scrollLeft = target.scrollLeft + e.deltaY;
    if (scrollLeft > scrollLeftMax) {
      scrollLeft = scrollLeftMax;
    } else if (scrollLeft < 0) {
      scrollLeft = 0;
    }
    target.scrollTo(scrollLeft, 0);
  };

  const onClose = (e: React.MouseEvent<HTMLElement>) => {
    e.stopPropagation();
    const target = e.currentTarget;
    const id = target.getAttribute('id');
    appDispatch({
      type: 'editor.close',
      data: id,
    });
  };

  return (
    <div className={cx('editor-manager-labels')} onWheel={onWheel}>
      { editors.map(o => (
        <div className={cx('editor-manager-label', {'is-active': o.id == activeId})} id={o.id} key={o.id} onClick={onLabelClick}>
          <span>{ o.fileName }</span>
          <div className={cx('icon-btn', {'is-show': o.originContent == o.content})} id={o.id} onClick={onClose}>
            <svg width="14px" height="14px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" strokeWidth="50" d="M529.066667 524.8l241.066666-241.066667c8.533333-8.533333 8.533333-21.333333 0-29.866666s-21.333333-8.533333-29.866666 0L499.2 494.933333 258.133333 253.866667c-8.533333-8.533333-21.333333-8.533333-29.866666 0s-8.533333 21.333333 0 29.866666L469.333333 524.8 228.266667 765.866667c-8.533333 8.533333-8.533333 21.333333 0 29.866666 4.266667 4.266667 10.666667 6.4 14.933333 6.4s10.666667-2.133333 14.933333-6.4L499.2 554.666667l241.066667 241.066666c4.266667 4.266667 10.666667 6.4 14.933333 6.4s10.666667-2.133333 14.933333-6.4c8.533333-8.533333 8.533333-21.333333 0-29.866666L529.066667 524.8z" /></svg>
          </div>
          <div className={cx('icon-btn', {'is-show': o.originContent != o.content})} id={o.id} onClick={onClose}>
            <svg width="14px" height="14px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M512 832c-177.066667 0-320-142.933333-320-320S334.933333 192 512 192s320 142.933333 320 320-142.933333 320-320 320z"></path></svg>
          </div>
        </div>
      )) }
    </div>
  );
}

export function EditorsView (props: { editorsState: IEditorsState }) {
  const { editorsState } = props;
  const { editors, activeId } = editorsState;

  return (
    <div className={cx('editor-manager')}>
      <EditorTabsView editorsState={editorsState} />
      <div className={cx('editor-manager-panels')}>
        { editors.map(o => <CodeEditorView key={o.id} editor={o} isActive={activeId == o.id} />) }
      </div>
    </div>
  );
}